{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2e92475f-6278-4551-b9c6-6fdadcc96dc3",
   "metadata": {},
   "source": [
    "# Sound Frequency Analysis\n",
    "\n",
    "## Sine Wave\n",
    "\n",
    "A sine wave is a function of time descibed by the **amplitude** ($A$), **frequency** ($f$), and the **phase** ($\\varphi$):\n",
    "\n",
    "$y(t)=A\\sin(2\\pi ft+\\varphi)$\n",
    "\n",
    "You can use NumPy to generate data samples of such sinusoids:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c84f447d-ebc6-48c8-9792-ae3e6e4ba68d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "NUM_SAMPLES_PER_SECOND = 44100\n",
    "\n",
    "\n",
    "def sine_wave(\n",
    "    frequency,\n",
    "    phase=0,\n",
    "    amplitude=1,\n",
    "    seconds=5,\n",
    "    samples_per_second=NUM_SAMPLES_PER_SECOND,\n",
    "):\n",
    "    t = np.linspace(0, seconds, seconds * samples_per_second)\n",
    "    return amplitude * np.sin(2 * np.pi * frequency * t + phase)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "974c514c-9f16-4845-ad73-68394b2f4bce",
   "metadata": {},
   "source": [
    "## Mixing Tones\n",
    "\n",
    "Let's mix a few sine waves to produce a sound comprised of multiple frequencies."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55a3c27c-acdf-4a96-bcc6-0613ac2068aa",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-warning\">\n",
    "    <strong>Note:</strong> The function below declares a variable as global, which is usually considered a bad programming practice. Global variables introduce tight-coupling between unrelated pieces of code, making them less flexible and vulnerable to unintended side effects. In this case, however, it's okay because you want to generate a tone interactively and then visualize and analyze it later.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f81b532-7e0d-4c3b-9c3f-380ab439d8dd",
   "metadata": {},
   "source": [
    "If you choose close frequency values, then you'll hear a peculiar [beating](https://en.wikipedia.org/wiki/Beat_(acoustics)) interference pattern:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "771b4d63-6158-4362-9e58-07ef8df3cfc3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ea19145b9a2e485898ae05841ec25ed2",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "interactive(children=(Checkbox(value=False, description='autoplay'), FloatSlider(value=220.0, description='fre…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Audio, display\n",
    "from ipywidgets import FloatSlider, interact\n",
    "\n",
    "tone = None\n",
    "\n",
    "\n",
    "@interact(\n",
    "    autoplay=False,\n",
    "    freq1=FloatSlider(min=1, max=1000, value=220),\n",
    "    freq2=FloatSlider(min=1, max=1000, value=440),\n",
    "    freq3=FloatSlider(min=1, max=1000, value=440.5),\n",
    ")\n",
    "def make_tone(autoplay, freq1, freq2, freq3):\n",
    "    global tone\n",
    "    tone = sine_wave(freq1) + sine_wave(freq2) + sine_wave(freq3)\n",
    "    display(Audio(tone, rate=NUM_SAMPLES_PER_SECOND, autoplay=autoplay))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f7d1a3f-ebdb-47fe-afc6-ab84c274765a",
   "metadata": {},
   "source": [
    "Here's the plot of the waveform:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c9b2c07f-fed7-44b5-b99b-2986f1d0683f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7f876cb0d370>]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.xlim(0, NUM_SAMPLES_PER_SECOND * 5)\n",
    "plt.plot(tone)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b6a6c5a-9531-4381-83f7-c34bac540c00",
   "metadata": {},
   "source": [
    "## Discrete Fourier Transform\n",
    "\n",
    "Here's the code that calculates the Discrete Fourier Transform and plots the frequency spectrum: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f6cebd8e-f589-4093-84f7-d6234ed97fcf",
   "metadata": {},
   "outputs": [],
   "source": [
    "from cmath import exp, pi\n",
    "\n",
    "\n",
    "def discrete_fourier_transform(x, k):\n",
    "    omega = 2 * pi * k / (N := len(x))\n",
    "    return sum(x[n] * exp(-1j * omega * n) for n in range(N))\n",
    "\n",
    "\n",
    "def plot_frequency_spectrum(\n",
    "    samples,\n",
    "    samples_per_second,\n",
    "    min_frequency=0,\n",
    "    max_frequency=None,\n",
    "):\n",
    "    num_bins = len(samples) // 2\n",
    "    nyquist_frequency = samples_per_second // 2\n",
    "\n",
    "    magnitudes = []\n",
    "    for k in range(num_bins):\n",
    "        magnitudes.append(abs(discrete_fourier_transform(samples, k)))\n",
    "\n",
    "    # Normalize magnitudes\n",
    "    magnitudes = [m / max(magnitudes) for m in magnitudes]\n",
    "\n",
    "    # Calculate frequency bins\n",
    "    bin_resolution = samples_per_second / len(samples)\n",
    "    frequency_bins = [k * bin_resolution for k in range(num_bins)]\n",
    "\n",
    "    plt.xlim(min_frequency, max_frequency or nyquist_frequency)\n",
    "    plt.bar(frequency_bins, magnitudes, width=bin_resolution)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41ab369d-8757-4de9-8578-1cd6ce3ffbf4",
   "metadata": {},
   "source": [
    "Speed up the calculations by taking only a small window of the sound wave:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "2faa2cda-16b9-41a0-b455-66bfdbfd29ed",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_frequency_spectrum(\n",
    "    tone[:1024], NUM_SAMPLES_PER_SECOND, max_frequency=2000\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
